# Import the base Model class from Flask-AppBuilder, which this model will inherit from.
from flask_appbuilder import Model
# Import various column types from SQLAlchemy to define the database schema.
from sqlalchemy import Column, Integer, String

# Import a custom audit mixin for tracking creation and modification details.
from myapp.models.helpers import AuditMixinNullable


# Get the metadata object from the base Model class. Metadata stores information about the database schema.
metadata = Model.metadata


# Define the Labels class, which maps to the 'labels' table in the database.
# This model implements a generic or polymorphic tagging system, where labels (tags)
# can be associated with different types of other models (e.g., 'prompt', 'dataset').
class Labels(Model, AuditMixinNullable):
    # Specifies the name of the database table for this model.
    __tablename__ = 'labels'
    # Defines the primary key column for the table.
    id = Column(Integer, primary_key=True)

    # Defines a column for the name of the label or tag itself.
    name = Column(String(256), nullable=False, comment='名称')
    # Defines a column for the ID of the related object being tagged.
    rel_id = Column(Integer, nullable=False, comment='关联id')
    # Defines a column for the type of the related object (e.g., 'prompt', 'dataset').
    # This allows the table to store labels for multiple different models.
    rel_type = Column(String(256), nullable=False, comment='关联类型')

    # Defines the official string representation of a Labels instance.
    def __repr__(self):
        """
        Returns a string representation of the model instance, primarily for debugging.
        This should return a string, so it should be `str(self.id)`.
        """
        return self.id

    # This is a static helper method to delete all labels for a specific object.
    @staticmethod
    def delete_without_commit(rel_id, rel_type):
        """
        Deletes all label associations for a given object ID and type from the database.
        This method performs the deletion but does not commit the transaction.
        The calling function is responsible for handling the session commit.
        :param rel_id: The ID of the related object.
        :param rel_type: The type string of the related object.
        """
        # Import the db session locally to avoid potential circular import issues.
        from myapp.app import db
        
        # Build and execute a delete query.
        db.session.query(Labels).filter(Labels.rel_id == rel_id).filter(
            Labels.rel_type == rel_type
        ).delete()
        return

    # This is a static helper method to add a list of labels to a 'prompt' object.
    @staticmethod
    def add_prompts_without_commit(prompt_id: int, labels: list):
        """
        Adds a list of labels for a specific 'prompt' object.
        This method adds the new label objects to the session but does not commit.
        The calling function must handle the transaction commit.
        :param prompt_id: The ID of the prompt to add labels to.
        :param labels: A list of strings, where each string is a label name.
        """
        # Import the db session locally.
        from myapp.app import db

        # Iterate over the provided list of label names.
        for label_name in labels:
            # Create a new Labels instance.
            item = Labels()
            # Set the properties for the new label association.
            item.rel_id = prompt_id
            item.rel_type = 'prompt'  # The type is hardcoded to 'prompt'.
            item.name = label_name
            # Add the new instance to the current database session.
            db.session.add(item)

    # This is a static helper method to update all labels for a 'prompt' object.
    @staticmethod
    def update_prompts_without_commit(prompt_id: int, labels: list):
        """
        Updates the labels for a specific 'prompt' object by replacing them.
        It first deletes all existing labels for the prompt and then adds the new ones.
        This method does not commit the transaction; the caller is responsible for the commit.
        :param prompt_id: The ID of the prompt to update.
        :param labels: The new list of label name strings for the prompt.
        """
        # Import the db session locally.
        from myapp.app import db

        # First, delete all existing labels associated with this prompt ID.
        # synchronize_session=False is a performance hint for bulk deletes.
        db.session.query(Labels).filter(Labels.rel_id == prompt_id).filter(
            Labels.rel_type == 'prompt'
        ).delete(synchronize_session=False)
        # Then, iterate over the new list of label names and add them.
        for label_name in labels:
            # Create a new Labels instance.
            item = Labels()
            # Set the properties for the new label association.
            item.rel_id = prompt_id
            item.rel_type = 'prompt'
            item.name = label_name
            # Add the new instance to the current database session.
            db.session.add(item)
